// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.

package nachos.machine;

import nachos.security.*;
import nachos.threads.*;

import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;

/**
 * This class implements a file system that redirects all requests to the host
 * operating system's file system.
 */
public class StubFileSystem implements FileSystem {
    /**
     * Allocate a new stub file system.
     *
     * @param	privilege      	encapsulates privileged access to the Nachos
     *				machine.
     * @param	directory	the root directory of the stub file system.
     */
    public StubFileSystem(Privilege privilege, File directory) {
		this.privilege = privilege;
		this.directory = directory;
    }
    
    public OpenFile open(String name, boolean truncate) {    	
		if (!checkName(name)){
			return null;
		}
		
		delay();
		    
		try {
			StubOpenFile sof = new StubOpenFile(name, truncate); 
		    return sof;
		}
		catch (IOException e) {
		    return null;
		}
    }
    
    public boolean remove(String name) {
		if (!checkName(name))
		    return false;
	
		delay();
	
		FileRemover fr = new FileRemover(new File(directory, name));
		privilege.doPrivileged(fr);
		return fr.successful;
    }

    private class FileRemover implements Runnable {
		public FileRemover(File f) {
		    this.f = f;
		}
	
		public void run() {
		    successful = f.delete();
		}
		
		public boolean successful = false;
		private File f;
    }

    private void delay() {
		long time = Machine.timer().getTime();
		int amount = 1000;
		ThreadedKernel.alarm.waitUntil(amount);
		Lib.assertTrue(Machine.timer().getTime() >= time+amount);
    }

    private class StubOpenFile extends OpenFileWithPosition {
		StubOpenFile(final String name, final boolean truncate) throws IOException {
		    super(StubFileSystem.this, name);
	
		    final File f = new File(directory, name);
	
		    if (openCount == maxOpenFiles)
			throw new IOException();
	
		    privilege.doPrivileged(new Runnable() {
			public void run() { getRandomAccessFile(f, truncate); }
		    });
	
		    if (file == null)
			throw new IOException();
	
		    open = true;
		    openCount++;
		}
	
		private void getRandomAccessFile(File f, boolean truncate) {
		    try {
				if (!truncate && !f.exists()){
					return;
				}
		
				file = new RandomAccessFile(f, "rw");
		
				if (truncate)
				    file.setLength(0);
		    }
		    catch (IOException e) {
		    }
		}
	
		public int read(int pos, byte[] buf, int offset, int length) {
		    if (!open)
			return -1;
		    
		    try {
			delay();
	
			file.seek(pos);
			return Math.max(0, file.read(buf, offset, length));
		    }
		    catch (IOException e) {
			return -1;
		    }
		}
		
		public int write(int pos, byte[] buf, int offset, int length) {
		    if (!open)
			return -1;
		    
		    try {
			delay();
			
			file.seek(pos);
			file.write(buf, offset, length);
			return length;
		    }
		    catch (IOException e) {
			return -1;
		    }
		}
	
		public int length() {
		    try {
		    	return (int) file.length();
		    }
		    catch (IOException e) {
		    	return -1;
		    }
		}
	
		public void close() {
			
		    if (open) {
			open = false;
			openCount--;
			}
			
		    try {
			file.close();
		    }
		    catch (IOException e) {
		    }
		    
		}
	
		private RandomAccessFile file = null;
		private boolean open = false;
    }

    private int openCount = 0;
    private static final int maxOpenFiles = 16;
    
    private Privilege privilege;
    private File directory;

    private static boolean checkName(String name) {
	char[] chars = name.toCharArray();

	for (int i=0; i<chars.length; i++) {
	    if (chars[i] < 0 || chars[i] >= allowedFileNameCharacters.length)
		return false;
	    if (!allowedFileNameCharacters[(int) chars[i]])
		return false;
	}
	return true;
    }

    private static boolean[] allowedFileNameCharacters = new boolean[0x80];

    private static void reject(char c) {
	allowedFileNameCharacters[c] = false;
    }

    private static void allow(char c) {
	allowedFileNameCharacters[c] = true;
    }

    private static void reject(char first, char last) {
	for (char c=first; c<=last; c++)
	    allowedFileNameCharacters[c] = false;
    }

    private static void allow(char first, char last) {
	for (char c=first; c<=last; c++)
	    allowedFileNameCharacters[c] = true;
    }

    static {
	reject((char) 0x00, (char) 0x7F);
	
	allow('A', 'Z');
	allow('a', 'z');
	allow('0', '9');
	
	allow('-');
	allow('_');
	allow('.');
	allow(',');
    }
}
